﻿//Message.cs
//Copyright (c) 2013 StockSharp LLC, all rights reserved.
//This code module is part of StockSharp library.
//This code is licensed under the GNU GENERAL PUBLIC LICENSE Version 3.
//See the file License.txt for the license details.
//More info on: http://stocksharp.com

namespace StockSharp.Algo.Testing
{
	using System;
	using System.Linq;
	using System.Collections.Generic;

	using Ecng.Common;

	using StockSharp.Algo.Storages;
	using StockSharp.Algo.Strategies;
	using StockSharp.BusinessEntities;

	/// <summary>
	/// Сообщение, содержащее рыночные данные(события) или команду на операцию с заявкой, или статус заявки.
	/// </summary>
	public abstract class Message
	{
		/// <summary>
		/// Момент времени, когда сообщение придет в конечную точку. 
		/// Может не совпадать с временой меткой данных в сообщении (latency).
		/// </summary>
		public DateTime Time { get; set; }

		/// <summary>
		/// Инструмент.
		/// </summary>
		public virtual Security Security
		{
			get { return null; }
		}

		/// <summary>
		/// Добавить сообщение.
		/// </summary>
		/// <param name="other">Другое сообщение, которое необходимо добавить.</param>
		public virtual void Append(Message other)
		{
		}

		/// <summary>
		/// Произвести эмуляцию сообщения в эмуляторе.
		/// </summary>
		/// <param name="emulator">Эмулятор.</param>
		public virtual void Emulate(MarketEmulator emulator)
		{
			// по умолчанию собыие просто "проскальзывает" эмулятор.
			emulator.EnqueueOutputMessage(this);
		}

		/// <summary>
		/// Тип сообщения.
		/// </summary>
		public abstract MarketDataTypes DataType { get; }

		/// <summary>
		/// Послать сообщение клиенту.
		/// </summary>
		/// <param name="trader">Шлюз.</param>
		public virtual void RaiseEvents(DataFeedTrader trader)
		{
		}

		/// <summary>
		/// Послать сообщение клиенту.
		/// </summary>
		/// <param name="emulator">Эмулятор.</param>
		public virtual void RaiseEvents(MarketEmulator emulator)
		{
		}

		/// <summary>
		/// Число рыночных событий в сообщении.
		/// </summary>
		public virtual int EventCount
		{
			get { return 1; }
		}

		/// <summary>
		/// Получить строковое представление.
		/// </summary>
		/// <returns>Строковое представление.</returns>
		public override string ToString()
		{
			return Time.ToString("yyyy/MM/dd HH:mm:ss.fff") + ":" + GetType().Name;
		}
	}

	class Message<T> : Message
	{
		protected Message(DateTime time, T evt)
		{
			Time = time;
			Events = new List<T>(new[] { evt });
			//Deltas = new List<T>();
			//Events.Add(evt); // - почему то с Add работает медленнее чтение(!?) (набивается очередь ивентов)
		}

		public Message(DateTime time, IEnumerable<T> evts)
		{
			Time = time;
			Events = new List<T>(evts);
			Deltas = new List<T>();
		}

		public List<T> Events { get; private set; }
		public List<T> Deltas { get; internal set; }

		public override void Append(Message other)
		{
			var typed = other as Message<T>;
			
			if (typed == null)
				throw new InvalidOperationException("Неверный тип добавляемых сообщений.");

			Events.AddRange(typed.Events);
		}

		public override MarketDataTypes DataType
		{
			get { return MarketDataTypes.EndOfData; }
		}

		protected T FirstEvent
		{
			get { return Events[0]; }
		}

		public override int EventCount
		{
			get { return Events.Count; }
		}

		public override string ToString()
		{
			return DataType + " " + FirstEvent + "(" + Events.Count + ")";
		}
	}

	sealed class TradeMessage : Message<Trade>
	{
		public TradeMessage(DateTime time, Trade trade)
			: base(time, trade)
		{
		}

		public TradeMessage(DateTime time, IEnumerable<Trade> trade)
			: base(time, trade)
		{
		}

		public override MarketDataTypes DataType
		{
			get { return MarketDataTypes.Trades; }
		}

		public override Security Security
		{
			get { return FirstEvent.Security; }
		}

		public override void Emulate(MarketEmulator emulator)
		{
			emulator.OnNewTrades(this);
		}

		public override void RaiseEvents(MarketEmulator emulator)
		{
			emulator.RaiseNewTrades(Events);
		}

		public override void RaiseEvents(DataFeedTrader trader)
		{
			trader.OnNewTrades(Events);
		}

		public override string ToString()
		{
			return Events.Aggregate("{0},T={1:HH:mm:ss.fff},".Put(DataType, FirstEvent.Time), (current,t)=>current+"Id={0},Pr={1},V={2},Side={3}\n".Put(t.Id, t.Price, t.Volume,t.OrderDirection));
		}
	}

	sealed class DepthMessage : Message<MarketDepth>
	{
		public DepthMessage(DateTime time, MarketDepth depth)
			: base(time, depth)
		{
		}

		public DepthMessage(DateTime time, MarketDepth depth, MarketDepth delta)
			: base(time, depth)
		{
			Deltas = new List<MarketDepth>(new []{delta});
		}

		public override MarketDataTypes DataType
		{
			get { return MarketDataTypes.MarketDepth; }
		}

		public override Security Security
		{
			get { return FirstEvent.Security; }
		}

		public override void Emulate(MarketEmulator emulator)
		{
			emulator.OnDepthChanged(this);
		}

		public override void RaiseEvents(MarketEmulator emulator)
		{
			emulator.RaiseDepthChanged(Events);
		}

		public override void RaiseEvents(DataFeedTrader trader)
		{
			trader.OnDepthsChanged(Events);
		}

		public override string ToString()
		{
			return DataType + ", T=" + FirstEvent.LastChangeTime.ToString("HH:mm:ss.fff") + FirstEvent.BestBid + "/" + FirstEvent.BestAsk + "(" + Events.Count + ")";
		}
	}

	sealed class SecurityChangeMessage : Message<SecurityChange>
	{
		public SecurityChangeMessage(DateTime time, SecurityChange change)
			: base(time, change)
		{
	
		}
		public override MarketDataTypes DataType
		{
			get { return MarketDataTypes.Security; }
		}

		public override Security Security
		{
			get { return FirstEvent.Security; }
		}

		public override void RaiseEvents(DataFeedTrader trader)
		{
			trader.OnSecurityChange(Events);
		}
	}

	sealed class TimeMessage : Message<DateTime>
	{
		public TimeMessage(DateTime time, DateTime data)
			: base(time, data)
		{
			
		}
		public override MarketDataTypes DataType
		{
			get { return MarketDataTypes.Time; }
		}

		public override void Emulate(MarketEmulator emulator)
		{
			emulator.OnTimeChanged(this);
		}

		public override void RaiseEvents(MarketEmulator emulator)
		{
			emulator.RaiseMarketTimeChanged();
		}

		public override void RaiseEvents(DataFeedTrader trader)
		{
			trader.OnMarketTimeChanged();
		}

		public override string ToString()
		{
			return DataType + ",T=" + FirstEvent.ToString("yyyy/MM/dd HH:mm:ss.fff");
		}
	}

	sealed class LastMessage : Message
	{
		public LastMessage()
		{
			Time = DateTime.MaxValue;
		}

		public override MarketDataTypes DataType
		{
			get { return MarketDataTypes.EndOfData; }
		}

		public override int EventCount
		{
			get { return 0; }
		}
	}

	sealed class OrderTraceMessage : Message<OrderTraceItem>
	{
		public Strategy Strategy { get; private set; }

		public OrderTraceMessage(DateTime time, Strategy strategy, OrderTraceItem item)
			: base(time, item)
		{
			Strategy = strategy;
		}

		public override MarketDataTypes DataType
		{
			get { return MarketDataTypes.OrderTrace; }
		}
	}

	class OrderTransactionMessage : Message<Order>
	{
		public OrderTransactionMessage(DateTime time, Order order, OrderTraceActions action, Order newOrder)
			: base(time, order)
		{
			if (newOrder != null)
				Events.Add(newOrder);

			Action = action;
		}

		public override MarketDataTypes DataType
		{
			get { return MarketDataTypes.OrderTransaction; }
		}

		public override Security Security
		{
			get { return FirstEvent.Security; }
		}
		
		/// <summary>
		/// Register,ReRegister,Cancel - действия по которым ордер попал в очередь (ActiveOrders)
		/// OrderFail - эмуляция ошибки установки ордера
		/// </summary>
		public OrderTraceActions Action { get; set; }

		public long TransactionId { get; set; }

		public Order Order
		{
			get { return Events[0]; }
		}

		public Order NewOrder
		{
			get { return Events[1]; }
		}

		public override void Emulate(MarketEmulator emulator)
		{
			emulator.OnOrderTransaction(this);
		}

		public override string ToString()
		{
			var result = Action + ",T=" + Time.ToString("HH:mm:ss.fff") + "," + Order;

			if (Events.Count > 1)
				result += "," + NewOrder;

			return result;
		}
	}

	class OrderStateMessage : Message<OrderTraceItem>
	{
		public OrderStateMessage(DateTime time, OrderTraceActions action, Order state)
			: this(time, new OrderTraceItem {Time = time, Action = action, Order = state})
		{
		}

		public OrderStateMessage(DateTime time, OrderTraceActions action, OrderFail fail)
			: this(time, new OrderTraceItem { Time = time, Action = action, Order = fail.Order, Fail = fail})
		{
		}

		public OrderStateMessage(DateTime time, OrderTraceItem item):base(time,item)
		{
		}

		public override MarketDataTypes DataType
		{
			get { return MarketDataTypes.OrderState; }
		}

		public override Security Security
		{
			get { return FirstEvent.Order.Security; }
		}

		public Order Order
		{
			get { return FirstEvent.Order; }
		}

		public override void Emulate(MarketEmulator emulator)
		{
			throw new InvalidOperationException("OrderStateMessage на входе не допустимо");
		}

		public override void RaiseEvents(MarketEmulator emulator)
		{
			emulator.RaiseOrderEvents(this);
		}

		public override string ToString()
		{
			var msg = string.Empty;

			foreach (var e in Events)
			{
				msg += e.Action + ",T=" + e.Order.LastChangeTime.ToString("HH:mm:ss.fff") + "," + e.Order;

				if (e.Action == OrderTraceActions.Failed)
					msg += "," + e.Fail.Error;

				msg += Environment.NewLine;
			}

			return msg;
		}
	}

	class MyTradeMessage : Message<MyTrade>
	{
		public MyTradeMessage(DateTime time, MyTrade trade)
			: base(time, trade)
		{
		}

		public override MarketDataTypes DataType
		{
			get { return MarketDataTypes.MyTrades; }
		}

		public override Security Security
		{
			get { return FirstEvent.Order.Security; }
		}

		public override void Emulate(MarketEmulator emulator)
		{
			throw new InvalidOperationException("MyTradeMessage на входе не допустимо.");
		}

		public override void RaiseEvents(MarketEmulator emulator)
		{
			emulator.RaiseNewMyTrades(Events);
		}

		public override string ToString()
		{
			return Events.Aggregate(DataType + " ", (current, e) => current + e.Trade.Time.ToString("HH:mm:ss.fff") + " " + e + Environment.NewLine);
		}
	}

	class OrderLogMessage : Message<OrderLogItem>
	{
		public OrderLogMessage(DateTime time, OrderLogItem item)
			: base(time, item)
		{
		}

		public override MarketDataTypes DataType
		{
			get { return MarketDataTypes.OrderLog; }
		}

		public override Security Security
		{
			get { return FirstEvent.Order.Security; }
		}

		public override void Emulate(MarketEmulator emulator)
		{
			emulator.OnOrderLog(this);
		}

		public override void RaiseEvents(MarketEmulator emulator)
		{
			emulator.RaiseOrderLog(Events);
		}

		public override void RaiseEvents(DataFeedTrader trader)
		{
			trader.OnOrderLogItems(Events);
		}

		public override string ToString()
		{
			return Events.Aggregate(DataType+", ", (current,e)=>current+FormatOrderLog(e)+"\n");
		}

		private static string FormatOrderLog(OrderLogItem e)
		{
			var msg = "{0:HH:mm:ss.fff},{1}".Put(
				e.Order.LastChangeTime,
			    e.IsRegistered() ? "Регист" : (e.IsMatched() ? "Сделка" : "Отмена"));

			if (e.IsMatched())
				msg += ",{0}".Put(e.Trade);

			msg += ",{0}".Put(e.Order);
			return msg;
		}
	}

	/// <summary>
	/// Процедура клиринга на бирже.
	/// </summary>
	class Clearing
	{
		/// <summary>
		/// Время проведения клиринга
		/// </summary>
		public DateTime Time { get; set; }

		/// <summary>
		/// Инструмент, для которого проводится клиринг.
		/// </summary>
		public Security Security { get; set; }

		/// <summary>
		/// Проводить ли очистку стакана.
		/// </summary>
		public bool ClearMarketDepth { get; set; }
	}

	class ClearingMessage : Message<Clearing>
	{
		public ClearingMessage(DateTime time, Clearing evt)
			: base(time, evt)
		{
		}

		public override MarketDataTypes DataType
		{
			get { return MarketDataTypes.Clearing; }
		}

		public override Security Security
		{
			get { return FirstEvent.Security; }
		}

		public override void Emulate(MarketEmulator emulator)
		{
			emulator.OnClearing(this);
		}
	}

	class SecurityDataInfo
	{
		public List<MarketDataTypes> Types { get; set; }
		public Security Security { get; set; }
		public DateTime Time { get; set; }
	}

	sealed class SecurityDataInfoMessage : Message<SecurityDataInfo>
	{
		public SecurityDataInfoMessage(DateTime time, SecurityDataInfo change)
			: base(time, change)
		{

		}
		public override MarketDataTypes DataType
		{
			get { return MarketDataTypes.Security; }
		}

		public override Security Security
		{
			get { return FirstEvent.Security; }
		}

		public override void Emulate(MarketEmulator emulator)
		{
			emulator.RaiseSecurityDataInfo(Events);
		}
	}
}